package fr.n7.khome.view;

import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;

import fr.n7.khome.core.Request;
import fr.n7.khome.core.Result;
import fr.n7.khome.dao.LodgingDAO;
import fr.n7.khome.dao.LodgingDAOMap;
import fr.n7.khome.model.Lodging;
import fr.n7.khome.model.LodgingFieldName;
import fr.n7.khome.model.LodgingInformationName;
import fr.n7.khome.util.FuzzyFormatException;
import fr.n7.khome.util.InvalidFuzzyValueException;
import fr.n7.khome.util.LodgingReader;

public class KHomeResultTableModel<Requests extends List<Request>>
extends AbstractTableModel
implements TableColumnModelListener{

	private static final long serialVersionUID = -7705293529873047170L;
	private Vector<LodgingFieldName> columns;
	private String[] columnNames;
	private LodgingDAO ldao;

	private Vector<Result> data;
	private KHomeTable resultTable;
	private KHomeTable requestTable;

	private float gLikelihoodOfFirst;
	private float gNecessityOfFirst;

	public KHomeResultTableModel(String[] columnNames, KHomeTable table, KHomeTable requestTable){
		super();

		this.columnNames = columnNames;
		this.resultTable = table;
		this.requestTable = requestTable;

		columns = new Vector<LodgingFieldName>();
		columns.add(LodgingFieldName.TYPE);
		columns.add(LodgingFieldName.PRICE);
		columns.add(LodgingFieldName.LIVE_SURFACE);
		columns.add(LodgingFieldName.FIELD_SURFACE);
		columns.add(LodgingFieldName.AGE);
		columns.add(LodgingFieldName.WORKPRICEPC);

		ldao = new LodgingDAOMap();
		try {
			List<Lodging> ls = LodgingReader.read("lodging.list");
			for(Lodging l : ls){
				ldao.create(l);
			}
		} catch (FuzzyFormatException e1) {
			e1.printStackTrace();
		} catch (InvalidFuzzyValueException e1) {
			e1.printStackTrace();
		}

		data = new Vector<Result>();
	}

	public int getColumnCount() {
		return columnNames.length;
	}

	public int getRowCount() {
		return data.size();
	}

	public String getColumnName(int col) {
		return columnNames[col];
	}

	public Object getValueAt(int row, int col) {
		Result r = data.get(row);
		if(col < LodgingInformationName.values().length){
			if(col == 0){
				return r.getLodging().getId();
			}else{
				return null;
			}
		}else{
			col=col-LodgingInformationName.values().length;
			Object field = r.getLodging().get(columns.get(col));
			return new ResultField(field, r.getLodging().getId(),
					r.getLikelihood().get(columns.get(col)), r.getGlikelihood(), r.getSumlikelihood(), gLikelihoodOfFirst,
					r.getNecessity().get(columns.get(col)), r.getGnecessity(), r.getSumnecessity(), gNecessityOfFirst);
		}
	}

	/*
	 * JTable uses this method to determine the default renderer/
	 * editor for each cell.  If we didn't implement this method,
	 * then the last column would contain text ("true"/"false"),
	 * rather than a check box.
	 */
	public Class<?> getColumnClass(int c) {
		return getValueAt(0, c).getClass();
	}

	/*
	 * Don't need to implement this method unless your table's
	 * editable.
	 */
	public boolean isCellEditable(int row, int col) {
		//Note that the data/cell address is constant,
		//no matter where the cell appears onscreen.
		return false;
	}

	/*
	 * Don't need to implement this method unless your table's
	 * data can change.
	 */
	public void setValueAt(Object value, int row, int col) {
		data.get(row).getLodging().set(columns.get(col), value);
		fireTableCellUpdated(row, col);
	}

	@Override
	public void columnAdded(TableColumnModelEvent e) {
	}

	@Override

	public void columnMarginChanged(ChangeEvent e) {
		Enumeration<TableColumn> columnEnum = requestTable.getColumnModel().getColumns();
		while(columnEnum.hasMoreElements()){
			TableColumn column = columnEnum.nextElement();
			int index = requestTable.getColumnModel().getColumnIndex(column.getIdentifier());
			column.setPreferredWidth(resultTable.getColumnModel().getColumn(index).getPreferredWidth());
		}
	}

	@Override
	public void columnMoved(TableColumnModelEvent e) {
		int fromIndex = e.getFromIndex();
		int toIndex = e.getToIndex();
		if(fromIndex != toIndex){
			int from = requestTable.convertColumnIndexToModel(fromIndex);
			int to = requestTable.convertColumnIndexToModel(toIndex);
			int rfrom = resultTable.convertColumnIndexToModel(fromIndex);
			int rto = resultTable.convertColumnIndexToModel(toIndex);
			if(from != rfrom || to != rto){
				requestTable.getColumnModel().moveColumn(e.getFromIndex(), e.getToIndex());
			}
		}
	}

	public void updateResultsWithRequests(Requests requests){
		Request r = requests.get(0);
		updateResultsWithRequest(r);
	}

	public void updateResultsWithRequest(Request request){
		Collection<Result> results = request.getPatternMatchingResults(ldao);
		data.clear();
		for(Result result : results){
			if(Float.compare(result.getGlikelihood(), 0f) != 0){
				data.add(result);
			}
		}
		if(data.size()>0){
			gLikelihoodOfFirst = data.get(0).getGlikelihood();
			gNecessityOfFirst = data.get(0).getGnecessity();
		}
		fireTableDataChanged();
	}

	public void updateResultsWithData(int row, int col, Object value){
		Request r = ((KHomeRequestTableModel<List<Request>>)requestTable.getModel()).getRequests().get(0);
		if(!r.getField(columns.get(col-LodgingInformationName.values().length)).equals(value)){
			r.setField(columns.get(col-LodgingInformationName.values().length),  value);
			updateResultsWithRequest(r);
		}
	}

	public void updateResultsWithImportance(int row, int col, Integer value){
		Request r = ((KHomeRequestTableModel<List<Request>>)requestTable.getModel()).getRequests().get(0);
		r.setFieldImportance(columns.get(col-LodgingInformationName.values().length),  value);
		updateResultsWithRequest(r);
	}

	public float getMaxGLikelyhood(){
		return gLikelihoodOfFirst;
	}

	@Override
	public void columnRemoved(TableColumnModelEvent e) {
	}

	@Override
	public void columnSelectionChanged(ListSelectionEvent e) {
	}
}
